package com.tinyengine.it.service.generator;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.tinyengine.it.config.GitConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Optional;

@Slf4j
@Component
@RequiredArgsConstructor
public class GitPushService {

    private final GitConfig gitConfig;
    // 统一的认证信息
    private UsernamePasswordCredentialsProvider credentialsProvider;

    @PostConstruct
    public void init() {
        // 初始化认证信息
        this.credentialsProvider = new UsernamePasswordCredentialsProvider(gitConfig.getUsername(), gitConfig.getPassword());
        log.info("Git 认证信息已初始化,用户名:{},密码:{}", gitConfig.getUsername(), gitConfig.getPassword());
    }


    /**
     * 同步并推送代码到 Git 仓库
     *
     * @param gitRepoUrl   Git 仓库地址 (HTTP URL)
     * @param localGitDir  本地 Git 仓库目录
     * @param generatorDir 生成的代码目录
     */
    public String syncAndPush(String gitRepoUrl, String localGitDir, String generatorDir) throws Exception {
        File gitDir = new File(localGitDir);
        Git git;

        // 如果本地不存在 Git 仓库，就进行克隆
        if (!new File(gitDir, ".git").exists()) {
            log.info("本地 Git 目录不存在，正在从 " + gitRepoUrl + " 克隆...");
            git = Git.cloneRepository()
                    .setURI(gitRepoUrl) // 使用 HTTP 地址
                    .setDirectory(gitDir)
                    .setCredentialsProvider(this.credentialsProvider) // 提供认证
                    .call();
            log.info("克隆完成: " + gitRepoUrl);
        } else {
            log.info("本地 Git 目录已存在，正在打开...");
            git = Git.open(gitDir);
        }

        // 配置 Git 提交者信息
        configureGitUser(git);

        // 确保切换到 quick-base 分支
        ensureBranch(git, "quick-base");

        // 拷贝 generatorDir 目录下的所有文件到本地 Git 目录
        log.info("正在复制文件到本地 Git 目录...");
        copyDirectory(new File(generatorDir), gitDir);

        // 使用 Git 添加文件
        git.add()
                .addFilepattern(".")
                .call();
        log.info("文件已添加到 Git 暂存区");

        // 创建提交信息
        String commitMessage = "快速开发平台(自动提交): " + LocalDateTimeUtil.format(LocalDateTime.now(), DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN));

        git.commit()
                .setMessage(commitMessage)
                .setCredentialsProvider(this.credentialsProvider)
                .call();
        log.info("提交完成: " + commitMessage);

        // 将修改推送到远程仓库
        log.info("正在推送到远程仓库...");
        git.push()
                .setRemote("origin")
                .setForce(Boolean.TRUE)//启用强制推送
                .setCredentialsProvider(this.credentialsProvider) // 使用用户名和密码进行认证
                .call();
        log.info("推送完成");

        git.close();
        log.info("=== 完成 ===");

        return "=== 完成 ===" + commitMessage;
    }

    /**
     * 配置 Git 用户信息
     *
     * @param git Git 实例
     */
    private static void configureGitUser(Git git) throws IOException {
        log.info("配置 Git 用户信息...");
        StoredConfig config = git.getRepository().getConfig();
        config.setString("user", null, "name", "AutoCommit");
        config.setString("user", null, "email", "autocommit@rjznzb.com");
        config.save();
        log.info("Git 用户信息已配置");
    }

    /**
     * 确保切换到指定的分支
     *
     * @param git    Git 实例
     * @param branch 分支名
     */
    private void ensureBranch(Git git, String branch) throws IOException {
        try {
            // 检查是否是空仓库
            boolean isEmptyRepository = isRepositoryEmpty(git);

            if (isEmptyRepository) {
                log.info("检测到空仓库，执行特殊初始化流程");
                initializeEmptyRepository(git, branch);
                return;
            }

            // 处理非空仓库
            handleNonEmptyRepository(git, branch);
        } catch (GitAPIException | IOException e) {
            log.error("处理分支失败", e);
            throw new IOException("无法处理分支: " + e.getMessage(), e);
        }
    }

    private boolean isRepositoryEmpty(Git git) throws GitAPIException {
        try {
            // 尝试获取远程引用
            Collection<Ref> remoteRefs = git.lsRemote()
                    .setCredentialsProvider(credentialsProvider)
                    .setHeads(true)
                    .call();

            // 如果没有任何引用，则是空仓库
            return remoteRefs.isEmpty();
        } catch (GitAPIException e) {
            log.warn("检查远程引用失败，假设是空仓库", e);
            return true;
        }
    }

    private void initializeEmptyRepository(Git git, String branch) throws GitAPIException, IOException {
        // 1. 创建初始提交（必须在创建分支前）
        createInitialCommit(git, branch);

        // 2. 创建并切换到新分支
        git.checkout()
                .setName(branch)
                .setCreateBranch(true)
                .call();

        // 3. 推送到远程
        git.push()
                .setRemote("origin")
                .setRefSpecs(new RefSpec("refs/heads/" + branch + ":refs/heads/" + branch))
                .setCredentialsProvider(credentialsProvider)
                .call();

        log.info("空仓库初始化完成，分支 {} 已创建并推送", branch);
    }

    private void handleNonEmptyRepository(Git git, String branch) throws GitAPIException, IOException {
        //  检查远程分支是否存在
        boolean remoteBranchExists = git.lsRemote()
                .setCredentialsProvider(credentialsProvider)
                .setHeads(true)
                .call()
                .stream()
                .anyMatch(ref -> ref.getName().endsWith("/" + branch));

        if (remoteBranchExists) {
            // 分支已存在，直接检出
            git.checkout()
                    .setName(branch)
                    .setStartPoint("origin/" + branch)
                    .call();
        } else {
            // 创建新分支基于默认分支
            String defaultBranch = getDefaultBranch(git);
            git.checkout()
                    .setName(branch)
                    .setCreateBranch(true)
                    .setStartPoint("origin/" + defaultBranch)
                    .call();

            // 推送到远程
            git.push()
                    .setRemote("origin")
                    .setRefSpecs(new RefSpec("refs/heads/" + branch + ":refs/heads/" + branch))
                    .setCredentialsProvider(credentialsProvider)
                    .call();
        }
    }

    // 获取仓库的默认分支（master或main）
    private String getDefaultBranch(Git git) throws GitAPIException {
        // 尝试获取远程HEAD指向的分支
        Optional<Ref> headRef = git.lsRemote()
                .setCredentialsProvider(credentialsProvider)
                .setRemote("origin")
                .call()
                .stream()
                .filter(ref -> ref.getName().equals("HEAD"))
                .findFirst();

        if (headRef.isPresent()) {
            String ref = headRef.get().getTarget().getName();
            return ref.substring(ref.lastIndexOf('/') + 1);
        }

        // 默认回退逻辑
        return "main"; // 或 "master"，根据你的仓库默认设置
    }

    // 创建初始提交
    private void createInitialCommit(Git git, String branch) throws IOException, GitAPIException {
        File workTree = git.getRepository().getWorkTree();

        // 确保至少有一个文件
        File readme = new File(workTree, "README.md");
        if (!readme.exists()) {
            try (FileWriter writer = new FileWriter(readme)) {
                writer.write("# " + branch + "\n\n自动生成的初始提交");
            }
        }

        // 添加所有文件
        git.add().addFilepattern(".").call();

        // 创建提交
        git.commit()
                .setMessage("初始提交 - 分支: " + branch)
                .call();
    }

    /**
     * 递归复制目录及文件
     *
     * @param sourceDir 源目录
     * @param targetDir 目标目录
     * @throws IOException
     */
    private static void copyDirectory(File sourceDir, File targetDir) throws IOException {
        if (!sourceDir.isDirectory()) {
            throw new IOException("源路径必须是目录");
        }
        if (!targetDir.exists()) {
            targetDir.mkdirs();
        }

        for (File file : sourceDir.listFiles()) {
            File targetFile = new File(targetDir, file.getName());
            if (file.isDirectory()) {
                copyDirectory(file, targetFile); // 递归复制子目录
            } else {
                java.nio.file.Files.copy(file.toPath(), targetFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
            }
        }
    }
}
